package orgInfo

import (
	"bytes"
	"crypto/x509"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
	"github.com/hyperledger/fabric-protos-go/msp"
	"github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
	"strconv"
	"time"
)

type Block struct {
	Number          uint64         `json:"number"`          //区块号
	PreviousHash    string         `json:"previousHash"`    //前区块Hash
	DataHash        string         `json:"dataHash"`        //交易体Hash
	TxNum           int            `json:"txNum"`           //区块内交易个数
	TransactionList []*Transaction `json:"transactionList"` //交易列表
	CreateTime      string         `json:"createTime"`      //区块生成时间
}

// 交易 -----一笔交易可以包含多个TransactionAction（提案响应）
type Transaction struct {
	TxId                  string               `json:"txId"`                  //交易ID
	Type                  string               `json:"type"`                  //交易类型
	Timestamp             string               `json:"timestamp"`             //交易创建时间
	ChannelId             string               `json:"channelId"`             //通道ID
	Creater               string               `json:"creater"`               //创建者
	TransactionActionList []*TransactionAction `json:"transactionActionList"` //交易提案列表
}

// TransactionAction 交易提案响应
type TransactionAction struct {
	Endorsements []string `json:"endorsements"` //背书组织ID列表
	ChaincodeId  string   `json:"chaincodeId"`  //链代码名称
	ReadSetList  []string `json:"readSetList"`  //读集
	WriteSetList []string `json:"writeSetList"` //写集
}

//查询指定区块信息
func QueryBlockByBlockNumber(led *ledger.Client, num int64) (*Block, error) {
	rawBlock, err := led.QueryBlock(uint64(num))
	if err != nil {
		fmt.Printf("QueryBlock return error: %s", err)
		return nil, err
	}
	//解析区块体
	txList := []*Transaction{}
	// 遍历区块中的多笔交易
	for i := range rawBlock.Data.Data {
		rawEnvelope, err := GetEnvelopeFromBlock(rawBlock.Data.Data[i])
		if err != nil {
			fmt.Printf("QueryBlock return error: %s", err)
			return nil, err
		}
		transaction, err := GetTransactionFromEnvelopeDeep(rawEnvelope)
		if err != nil {
			fmt.Printf("QueryBlock return error: %s", err)
			return nil, err
		}
		txList = append(txList, transaction)
	}
	block := Block{
		Number:          rawBlock.Header.Number,
		PreviousHash:    hex.EncodeToString(rawBlock.Header.PreviousHash),
		DataHash:        hex.EncodeToString(rawBlock.Header.DataHash),
		TxNum:           len(rawBlock.Data.Data),
		TransactionList: txList,
		CreateTime:      txList[0].Timestamp, //无法获取出块时间，用第一笔交易的时间代替（参考fabric官方浏览器）
	}

	return &block, nil
}

//根据txid查询交易信息
func QueryTxInfoByTxID(led *ledger.Client, txid string) (*Transaction, error) {
	fmt.Println("QueryTxInfoByTxID---txid", txid)
	// 用于判断是否是查询的为第一个区块中的交易，第一个区块中的交易txid为空
	if txid == "" {
		block, _ := QueryBlockByBlockNumber(led, 1)
		return block.TransactionList[0], nil
	} else {
		processedTransaction, err := led.QueryTransaction(fab.TransactionID(txid))
		if err != nil {
			fmt.Printf("QueryTx return error: %s", err)
			return nil, err
		}
		tx, err := GetTransactionFromEnvelopeDeep(processedTransaction.TransactionEnvelope)
		if err != nil {
			fmt.Printf("QueryTx return error: %s", err)
			return nil, err
		}
		return tx, nil
	}

}

//获取区块体数据item
func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
	var err error
	env := &common.Envelope{}
	if err = proto.Unmarshal(data, env); err != nil {
		fmt.Printf("block unmarshal err: %s", err)
	}
	return env, nil
}

// GetPayloadFromEnvelope 解析Envelope.payload
func GetPayloadFromEnvelope(envelope *common.Envelope) (payload *common.Payload, err error) {
	payload = &common.Payload{}
	err = proto.Unmarshal(envelope.Payload, payload)
	if err != nil {
		fmt.Printf("block unmarshal err: %s", err)
		return nil, err
	}
	return payload, nil
}

// GetChannelHeaderFromPayload 解析Payload.Header.ChannelHeader
func GetChannelHeaderFromPayload(payload *common.Payload) (channelHeader *common.ChannelHeader, err error) {
	channelHeader = &common.ChannelHeader{}
	err = proto.Unmarshal(payload.Header.ChannelHeader, channelHeader)
	if err != nil {
		fmt.Printf("block unmarshal err: %s\n", err)
		return nil, err
	}
	return channelHeader, nil
}

// GetSignatureHeaderFromPayload 解析Payload.Header.SignatureHeader
func GetSignatureHeaderFromPayload(payload *common.Payload) (signatureHeader *common.SignatureHeader, err error) {
	signatureHeader = &common.SignatureHeader{}
	err = proto.Unmarshal(payload.Header.SignatureHeader, signatureHeader)
	if err != nil {
		fmt.Printf("block unmarshal err: %s\n", err)
		return nil, err
	}
	return signatureHeader, nil
}

// GetCreatorFromSignatureHeader 解析ChannelHeader.Creator--从证书中解析出交易创建者
func GetCreatorFromSignatureHeader(signatureHeader *common.SignatureHeader) (creator string, err error) {
	certStart := bytes.IndexAny(signatureHeader.Creator, "-----BEGIN")
	if certStart == -1 {
		fmt.Println("No certificate found")
		return "", err
	}
	certText := signatureHeader.Creator[certStart:]
	bl, _ := pem.Decode(certText)
	if bl == nil {
		fmt.Println("Could not decode the PEM structure")
		return "", errors.New("Could not decode the PEM structure")
	}
	cert, err := x509.ParseCertificate(bl.Bytes)
	if err != nil {
		fmt.Println("ParseCertificate failed")
		return "", errors.New("ParseCertificate failed")
	}
	creator = cert.Subject.CommonName
	return creator, nil
}

//GetTransactionFromEnvelopeDeep 解析出一笔交易 深度递归解析Transaction
func GetTransactionFromEnvelopeDeep(rawEnvelope *common.Envelope) (*Transaction, error) {
	//解析payload
	rawPayload, err := GetPayloadFromEnvelope(rawEnvelope)
	if err != nil {
		return nil, err
	}
	//解析channelHeader
	channelHeader, err := GetChannelHeaderFromPayload(rawPayload)
	if err != nil {
		return nil, err
	}
	// 解析SignatureHeader
	signatureHeader, err := GetSignatureHeaderFromPayload(rawPayload)
	if err != nil {
		return nil, err
	}
	// 从signatureHeader解析出creator
	creator, err := GetCreatorFromSignatureHeader(signatureHeader)
	if err != nil {
		return nil, err
	}
	//将解析出来的交易信息绑定
	transaction := &Transaction{}
	transaction.TxId = channelHeader.TxId
	transaction.Type = strconv.Itoa(int(channelHeader.Type))
	transaction.Creater = creator
	transaction.Timestamp = time.Unix(channelHeader.Timestamp.Seconds, 0).Format("2006-01-02 15:04:05")
	transaction.ChannelId = channelHeader.ChannelId
	transactionActionList := []*TransactionAction{}
	// Type =="1" 表示通道启动区块，没有交易提案
	if transaction.Type == "1" {
		transactionActionList = append(transactionActionList, &TransactionAction{
			Endorsements: []string{},
			ChaincodeId:  "",
			ReadSetList:  []string{},
			WriteSetList: []string{},
		})
		transaction.TransactionActionList = transactionActionList
		return transaction, nil
	} else {
		//解析Transaction
		transactionObj := &peer.Transaction{}
		err = proto.Unmarshal(rawPayload.Data, transactionObj)
		if err != nil {
			fmt.Printf("block unmarshal err: %s\n", err)
		}
		//解析该交易的交易提案 transactionAction
		for i := range transactionObj.Actions {
			transactionAction, err := GetTransactionActionFromTransactionDeep(transactionObj.Actions[i])
			if err != nil {
				fmt.Printf("block unmarshal err: %s\n", err)
			}
			transactionActionList = append(transactionActionList, transactionAction)
		}
		transaction.TransactionActionList = transactionActionList
		return transaction, nil
	}

}

// GetTransactionActionFromTransactionDeep 解析TransactionAction
func GetTransactionActionFromTransactionDeep(transactionAction *peer.TransactionAction) (*TransactionAction, error) {

	//解析ChaincodeActionPayload 1
	ChaincodeActionPayload := &peer.ChaincodeActionPayload{}
	err := proto.Unmarshal(transactionAction.Payload, ChaincodeActionPayload)
	if err != nil {
		fmt.Printf("block unmarshal err: %s\n", err)
	}
	//解析ProposalResponsePayload 1.2
	ProposalResponsePayload := &peer.ProposalResponsePayload{}
	ChaincodeAction := &peer.ChaincodeAction{}
	chaincodeId := ""
	NsReadWriteSetList := []*rwset.NsReadWriteSet{}
	ReadWriteSetList := []*kvrwset.KVRWSet{}
	readSetList := []string{}
	writeSetList := []string{}
	if ChaincodeActionPayload.GetAction() != nil {
		err = proto.Unmarshal(ChaincodeActionPayload.Action.ProposalResponsePayload, ProposalResponsePayload)
		if err != nil {
			fmt.Printf("block unmarshal err: %s", err)
		}
		//解析ChaincodeAction 1.2.1
		err = proto.Unmarshal(ProposalResponsePayload.Extension, ChaincodeAction)
		if err != nil {
			fmt.Printf("block unmarshal err: %s", err)
		}
		chaincodeId = ChaincodeAction.ChaincodeId.Name
		//解析TxReadWriteSet	1.2.1.1
		TxReadWriteSet := &rwset.TxReadWriteSet{}
		err = proto.Unmarshal(ChaincodeAction.Results, TxReadWriteSet)
		if err != nil {
			fmt.Printf("block unmarshal err: %s", err)
		}
		//解析TxReadWriteSet	1.2.1.1.1
		for i := range TxReadWriteSet.NsRwset {
			ReadWriteSet := &kvrwset.KVRWSet{}
			//解析ReadWriteSet	1.2.1.1.1.1
			err = proto.Unmarshal(TxReadWriteSet.NsRwset[i].Rwset, ReadWriteSet)
			if err != nil {
				fmt.Printf("block unmarshal err: %s", err)
			}
			//解析读集
			for i := range ReadWriteSet.Reads {
				readSetJsonStr, err := json.Marshal(ReadWriteSet.Reads[i])
				if err != nil {
					fmt.Printf("block unmarshal err: %s", err)
				}
				readSetList = append(readSetList, string(readSetJsonStr))
			}
			//解析写集
			for i := range ReadWriteSet.Writes {
				writeSetItem := map[string]interface{}{
					"Key":      ReadWriteSet.Writes[i].GetKey(),
					"Value":    string(ReadWriteSet.Writes[i].GetValue()),
					"IsDelete": ReadWriteSet.Writes[i].GetIsDelete(),
				}

				writeSetJsonStr, err := json.Marshal(writeSetItem)
				if err != nil {
					fmt.Printf("block unmarshal err: %s", err)
				}
				writeSetList = append(writeSetList, string(writeSetJsonStr))
			}
			ReadWriteSetList = append(ReadWriteSetList, ReadWriteSet)
			NsReadWriteSetList = append(NsReadWriteSetList, TxReadWriteSet.NsRwset[i])
		}

	} else {
		chaincodeId = "没有交易数据"
	}
	//log.Println("数据:"+fmt.Sprintf("%s\n",ChaincodeActionPayload.Action.GetEndorsements()[0] ))
	//解析Endorsements 1.3
	endorsements := []string{}
	if ChaincodeActionPayload.Action.GetEndorsements() != nil {
		for i := range ChaincodeActionPayload.Action.GetEndorsements() {
			endorser := &msp.SerializedIdentity{}
			err = proto.Unmarshal(ChaincodeActionPayload.Action.Endorsements[i].Endorser, endorser)
			if err != nil {
				fmt.Printf("block unmarshal err: %s", err)
			}
			endorsements = append(endorsements, string(endorser.Mspid))
		}
	}
	transactionActionObj := TransactionAction{
		Endorsements: endorsements,
		ChaincodeId:  chaincodeId,
		ReadSetList:  readSetList,
		WriteSetList: writeSetList,
	}
	return &transactionActionObj, nil
}
